<?php

namespace App\Kuafu\Common\Service;

use App\Exceptions\BaseException;
use App\Kuafu\Audit\Enum\AuditEventCodeEnum;
use App\Kuafu\Audit\Service\AuditService;
use App\Kuafu\Base\Model\BaseModel;
use App\Kuafu\Common\Enum\CommonCodeEnum;
use App\Kuafu\Base\Service\BaseService;
use App\Kuafu\Config\Enum\ConfigCacheEnum;
use App\Kuafu\Config\Model\ConfigModel;
use App\Kuafu\CustomerCoupon\Enum\CodeEnum;
use App\Kuafu\Shop\Enum\AreaCodeEnum;
use App\Kuafu\Shop\Enum\ShopCodeEnum;
use App\Kuafu\Shop\Model\AreaModel;
use App\Kuafu\User\Exceptions\UserException;
use App\Kuafu\User\Service\AuthService;
use App\Kuafu\Wechat\Service\WechatMiniProgramService;
use App\Library\FeiShu\SendFs;
use Doctrine\DBAL\Exception;
use Illuminate\Http\Request;
use Illuminate\Redis\Connections\PhpRedisConnection;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Schema;
use Symfony\Component\HttpFoundation\Request as RequestAlias;

class CommonService extends BaseService
{
    /**
     * 判断金额合法
     * @param $num number 金额
     * @param $position int 小数点位数
     * @return array
     */
    public static function checkNumberPoint($num, int $position): array
    {
        $len = strlen(substr(strrchr($num, "."), 1));
        if ($len > $position) {
            return self::returnData(CommonCodeEnum::ERR_PRODUCT_PRICE_TWO_POS);
        } else {
            return self::returnData(CommonCodeEnum::OK);
        }
    }

    public static function joinImageUrl(string $imageUrl = '', string $otherUrl = '')
    {
        $baseUrl  = rtrim(config("other.IMAGE_URL"), '/');
        $imageUrl = ltrim($imageUrl, '/');

        return trim($baseUrl . '/' . $otherUrl . $imageUrl);
    }

    public static function getRealIp()
    {
        if (getenv('HTTP_CLIENT_IP')) {
            $ip = getenv('HTTP_CLIENT_IP');
        }
        if (getenv('HTTP_X_REAL_IP')) {
            $ip = getenv('HTTP_X_REAL_IP');
        } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
            $ip  = getenv('HTTP_X_FORWARDED_FOR');
            $ips = explode(',', $ip);
            $ip  = $ips[0];
        } elseif (getenv('REMOTE_ADDR')) {
            $ip = getenv('REMOTE_ADDR');
        } else {
            $ip = '0.0.0.0';
        }
        return $ip;
    }


    //订单来源 3
    public static function decodeRules($rules, $arr = [])
    {
        if (empty($arr)) {
            // 这个数组，必须降序排列
            $arr = [
                CodeEnum::COUPON_SOURCE_16,
                CodeEnum::COUPON_SOURCE_8,
                CodeEnum::COUPON_SOURCE_4,
                CodeEnum::COUPON_SOURCE_2,
                CodeEnum::COUPON_SOURCE_1,
            ];
        }
        $res = [];
        foreach ($arr as $rule_id) {

            if ($rule_id > $rules) {
                continue;
            }
            $rules -= $rule_id;
            $res[] = $rule_id;
        }
        return $res;
    }


    /**
     * 二维数组去重
     * @param $arr
     * @param $key
     * @return array
     */
    public static function arrayUnset($arr, $key)
    {
        //建立一个目标数组
        $res = array();
        foreach ($arr as $value) {
            //查看有没有重复项
            if (isset($res[$value[$key]]) || empty($value['coupon_rel_by_activity'])) {
                unset($value[$key]);  //有：销毁
            } else {
                $res[$value[$key]] = $value;
            }
        }
        return $res;
    }

    public static function arrayCustomerUnset($arr, $key)
    {
        //建立一个目标数组
        $res = array();
        foreach ($arr as $value) {
            //查看有没有重复项
            if (isset($res[$value[$key]])) {
                unset($value[$key]);  //有：销毁
            } else {
                $res[$value[$key]] = $value;
            }
        }
        return $res;
    }

    /**
     * 门店 Code 补 0
     *
     * @param int $shopCode
     * @return string
     */
    public static function zeroFill(?int $shopCode)
    {
        return str_pad($shopCode, ShopCodeEnum::CODE_TEXT[ShopCodeEnum::LEADING_ZERO], "0",
            STR_PAD_LEFT);
    }

    /**
     * curl 请求
     *
     * @param string $url 请求的url地址
     * @param string $method
     * @param array $param 请求的参数
     * @param array $header
     * @param int $timeout
     * @param bool $log 是否启用日志
     * @return mixed
     */
    public static function customCurl(
        string $url,
        string $method,
        array $param = array(),
        array $header = array(),
        int $timeout = 10,
        bool $log = true
    ) {
        $ch = curl_init();
        switch ($method) {
            case RequestAlias::METHOD_GET:
                if (!empty($param)) {
                    $url .= '?' . http_build_query($param);
                }
                break;
            case RequestAlias::METHOD_POST:
                curl_setopt($ch, CURLOPT_POST, 1); //POST
                curl_setopt($ch, CURLOPT_POSTFIELDS, $param); //post数据
                break;
        }

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); //设置超时时间
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回原生的（Raw）输出
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

        if ($header) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        }
        $data     = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $errNo    = (int)curl_errno($ch);
        curl_close($ch);
        if ($log) {
            Log::info("curl", [
                'url'          => $url,
                'method'       => $method,
                'params'       => $param,
                'errNo'        => $errNo,
                'responseCode' => $httpCode,
                'response'     => $data
            ]);
        }
        return [$url, $method, $param, $errNo, $httpCode, $data];
    }

    /**
     * @param string $lat1 纬度1
     * @param string $lng1 经度1
     * @param string $lat2 纬度2
     * @param string $lng2 经度2
     * @return false|float
     */
    public static function getDistance(string $lat1, string $lng1, string $lat2, string $lng2)
    {
        if (empty($lat1) || empty($lng1) || empty($lat2) || empty($lng2)) {
            return false;
        }

        $radLat1 = deg2rad($lat1);//deg2rad()函数将角度转换为弧度
        $radLat2 = deg2rad($lat2);
        $radLng1 = deg2rad($lng1);
        $radLng2 = deg2rad($lng2);
        $a       = $radLat1 - $radLat2;
        $b       = $radLng1 - $radLng2;
        $s       = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2))) * 6378.137;
        return round($s, 2);//返回公里数
    }


    /**
     * @param string $name
     * @return PhpRedisConnection
     */
    public static function getRedis(string $name = 'default')
    {
        return Redis::connection($name);
    }


    public static function getStartTime($date = ''): string
    {
        return $date ? $date . ' 00:00:00' : '';
    }

    public static function getEndTime($date = ''): string
    {
        return $date ? $date . ' 23:59:59' : '';
    }


    /**
     * 飞书 ｜ 日志 处理异常
     *
     * @param string $title
     * @param \Exception $exception
     * @param array $logParams
     * @param bool $logError
     */
    public static function fsLogException(
        string $title,
        \Exception $exception,
        array $logParams = [],
        bool $logError = false
    ) {
        $refException = new \ReflectionClass($exception);
        $properties   = $refException->getProperties(\ReflectionProperty::IS_PROTECTED);
        $return       = [];
        foreach ($properties as $property) {
            // 为了兼容反射私有属性
            $property->setAccessible(true);
            // 将得到的类属性同具体的实例绑定解析，获得实例上的属性值
            $return[$property->getName()] = $property->getValue($exception);
        }

        $msgCollection = new Collection(array_merge($return, $logParams, [
            'time'  => date('Y-m-d H:i:s', time()),
            'trace' => $exception->getTraceAsString()
        ]));
        SendFs::newRobot()
              ->setTitleContent($title, $msgCollection->toJson(JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT))
              ->send();

        if ($logError) {
            Log::error($title, $msgCollection->toArray());
        }
    }

    /**
     * 操作日志写入
     *
     * @param string $type
     * @param array $original
     * @param array $data
     * @param string $tableName
     * @param array $extra
     * @param string $connection
     * @param bool $fsNotify
     * @param bool $logNotify
     * @throws Exception
     * @throws UserException
     */
    public static function fsLogOperate(
        string $type,
        array $original,
        array $data,
        string $tableName,
        array $extra = [],
        string $connection = '',
        bool $fsNotify = true,
        bool $logNotify = false
    ) {

        $tableComment = new Collection();
        $columns      = DB::connection($connection)->getDoctrineSchemaManager()->listTableDetails($tableName)
                          ->getColumns();
        foreach ($columns as $column) {
            $tableComment->push((object)[
                'name'    => $column->getName(),
                'comment' => $column->getComment()
            ]);
        }

        try {
            $username = AuthService::getAuthUser()['username'] ?? '';
        } catch (\Exception $e) {
            $username = '';
        }

        $columnIndexComment = $tableComment->keyBy('name');

        $content       = vsprintf('[ %s ] [ %s ] [ %s ]' . PHP_EOL . PHP_EOL, [
            AuditEventCodeEnum::EVENT_TYPE_TEXT[$type],
            $username,
            date('Y-m-d H:i:s')
        ]);
        $content       .= '[ 变更信息 ]' . PHP_EOL;
        $changeContent = '';
        $data          = Collect($data)->except(['updatedAt', 'updatedByUid', 'createdAt', 'createdByUid'])->toArray();
        foreach ($data as $key => $value) {
            if ((isset($original[$key]) && $original[$key] != $value) || empty($original)) {
                $changeContent .= vsprintf(PHP_EOL . '### 字段: [ %s ] [ %s ]' . PHP_EOL, [
                    $key,
                    $columnIndexComment[$key]->comment
                ]);
                $changeContent .= vsprintf(' -------------- 变更前: [ %s ]  ' . PHP_EOL . ' -------------- 变更后: [ %s ] ' . PHP_EOL,
                    [
                        $original[$key] ?? '',
                        $value
                    ]);
            }
        }

        if ($changeContent) {
            $content .= $changeContent . PHP_EOL . '[ 附加信息 ]' . PHP_EOL . PHP_EOL;
            $content .= collect($extra)->toJson(JSON_UNESCAPED_UNICODE);

            if ($fsNotify) {
                AuditService::sendNotify($type, $content);
            }

            if ($logNotify) {
                Log::info(AuditEventCodeEnum::EVENT_TYPE_TEXT[$type], ['content' => $content]);
            }
        }

    }


    /**
     * w转周几文字
     * @param $w
     * @return string
     */
    public static function wToWord($w)
    {
        switch ($w) {
            case 0:
                $s = '周日';
                break;
            case 1:
                $s = '周一';
                break;
            case 2:
                $s = '周二';
                break;
            case 3:
                $s = '周三';
                break;
            case 4:
                $s = '周四';
                break;
            case 5:
                $s = '周五';
                break;
            case 6:
                $s = '周六';
                break;
            default:
                $s = '错误的w';
                break;
        }

        return $s;
    }

    public static function checkIpAddress($ipaddres)
    {
        $preg = "/\A((([0-9]?[0-9])|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))\.){3}(([0-9]?[0-9])|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))\Z/";
        if (preg_match($preg, $ipaddres)) {
            return true;
        } else {
            return false;
        }
    }

    public static function getAreaCodeByArea(array $area)
    {
        $provinceModel = AreaModel::query()
                                  ->where('level', AreaCodeEnum::AREA_PROVINCE)
                                  ->where('cnFullName', ($area[AreaCodeEnum::AREA_ARRAY_INDEX_0] ?? ''))
                                  ->first();
        if ($provinceModel == null) {
            $provinceCode = 0;
        } else {
            $provinceCode = $provinceModel->areaId;
        }
        $cityModel = AreaModel::query()
                              ->where('level', AreaCodeEnum::AREA_CITY)
                              ->where('cnFullName', ($area[AreaCodeEnum::AREA_ARRAY_INDEX_1] ?? ''))
                              ->where('areaPid', $provinceCode)
                              ->first();
        if ($cityModel == null) {
            $cityCode = 0;
        } else {
            $cityCode = $cityModel->areaId;
        }
        $regionModel = AreaModel::query()
                                ->where('level', AreaCodeEnum::AREA_AREA)
                                ->where('cnFullName', ($area[AreaCodeEnum::AREA_ARRAY_INDEX_2] ?? ''))
                                ->where('areaPid', $cityCode)
                                ->first();
        if ($regionModel == null) {
            $regionCode = 0;
        } else {
            $regionCode = $regionModel->areaId;
        }
        return [
            [
                'level' => AreaCodeEnum::AREA_PROVINCE,
                'code'  => $provinceCode,
            ],
            [
                'level' => AreaCodeEnum::AREA_CITY,
                'code'  => $cityCode,
            ],
            [
                'level' => AreaCodeEnum::AREA_AREA,
                'code'  => $regionCode,
            ]
        ];
    }

    /**
     * 获取小程序投诉跳转飞书应用内链接
     * @param int $id
     * @return string
     */
    public static function getComplainInfoWenAppUrl(int $id): string
    {
        $appid = config("feishu.center.appID");
        $path  = "/complain/info/{$id}?isMainModule=1";
        return "https://applink.feishu.cn/client/web_app/open?appId={$appid}&path=$path";
    }

    /**
     * 通过当前商品名称匹配是否打印标签：默认0-不打印 1-饮品打印 2-非饮品打印
     * @param string $name
     * @param int $source 1-堂食 3-美团 4-饿了么
     * @param string $type
     * @param string $key
     * @return array
     */
    public static function getIsPrintLabelByName(
        string $name,
        int $source,
        string $type = 'pos',
        string $key = 'productPrintLabel'
    ): array {
        $config       = Cache::remember(ConfigCacheEnum::getConfigType($type, $key), getSwitchCacheTime(300),
            function () use ($type, $key) {
                return json_decode(ConfigModel::query()
                                              ->where('type', $type)
                                              ->where('key', $key)
                                              ->value('value') ?? '', true);
            });
        $isPrintLabel = $printLabelType = 0;
        if ($config && $name) {
            collect($config)
                ->where('source', $source)
                ->map(function ($item) use ($name, &$isPrintLabel, &$printLabelType) {
                    if (isset($item['data']) && is_array($item['data'])) {
                        foreach ($item['data'] as $v) {
                            if (strpos($name, $v) !== false) {
                                $isPrintLabel = 1;
                                break;
                            }
                        }
                    }
                    if (isset($item['datas']) && is_array($item['datas'])) {
                        foreach ($item['datas'] as $v) {
                            if (isset($v['data']) && is_array($v['data'])) {
                                foreach ($v['data'] as $data) {
                                    if (strpos($name, $data) !== false) {
                                        $isPrintLabel   = 1;
                                        $printLabelType = $v['type'];
                                        break 2;
                                    }
                                }
                            }
                        }
                    }
                });
        }
        return ['isPrintLabel' => $isPrintLabel, 'printLabelType' => $printLabelType];
    }

    /**
     * @param object $params
     * @return array|void
     * @throws BaseException
     */
    public static function getImageBySceneKey(object $params)
    {
        try {
            return [
                "imgUrl" => WechatMiniProgramService::getImageBySceneKey($params->sceneKey)
            ];
        } catch (\Exception $exception) {
            handleException($exception);
        }
    }
}
